//
//  GLView.m
//  Part2Project
//
//  Created by jeff on 4/30/09.
//  Copyright Jeff LaMarche 2009. All rights reserved.
//

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "GLView.h"


@interface GLView (private)

- (id)initGLES;
- (BOOL)createFramebuffer;
- (void)destroyFramebuffer;

@end

@implementation GLView

@synthesize animationInterval;
+ (Class) layerClass
{
	return [CAEAGLLayer class];
}
-(id)initWithFrame:(CGRect)frame
{
	self = [super initWithFrame:frame];
	if(self != nil)
	{
		self = [self initGLES];
	
	}
	return self;
}

- (id)initWithCoder:(NSCoder*)coder
{
	if((self = [super initWithCoder:coder]))
	{
		self = [self initGLES];
	}	
	return self;
}

-(id)initGLES
{
		
	CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer;
	
	// Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
	eaglLayer.opaque = YES;
	eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
										[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
										kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
										nil];
	
	// Create our EAGLContext, and if successful make it current and create our framebuffer.
	context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
	if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer])
	{
		[self release];
		return nil;
	}
	
	// Default the animation interval to 1/60th of a second.
	animationInterval = 1.0 / kRenderingFrequency;
	return self;
}

-(GLViewController *)controller
{
	return nil;
}

-(void)setController:(GLViewController *)d
{

	//controllerSetup = ![controller respondsToSelector:@selector(setupView:)];
}

// If our view is resized, we'll be asked to layout subviews.
// This is the perfect opportunity to also update the framebuffer so that it is
// the same size as our display area.
-(void)layoutSubviews
{
	[EAGLContext setCurrentContext:context];
	[self destroyFramebuffer];
	[self createFramebuffer];
	[self drawView];
}

- (BOOL)createFramebuffer
{
	// Generate IDs for a framebuffer object and a color renderbuffer
	glGenFramebuffersOES(1, &viewFramebuffer);
	glGenRenderbuffersOES(1, &viewRenderbuffer);
	
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
	// This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)
	// allowing us to draw into a buffer that will later be rendered to screen whereever the layer is (which corresponds with our view).
	[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
	
	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
	
	// For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
	glGenRenderbuffersOES(1, &depthRenderbuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
	glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);

	if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
	{
		NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
		return NO;
	}
	
	return YES;
}

// Clean up any buffers we have allocated.
- (void)destroyFramebuffer
{
	glDeleteFramebuffersOES(1, &viewFramebuffer);
	viewFramebuffer = 0;
	glDeleteRenderbuffersOES(1, &viewRenderbuffer);
	viewRenderbuffer = 0;
	
	if(depthRenderbuffer)
	{
		glDeleteRenderbuffersOES(1, &depthRenderbuffer);
		depthRenderbuffer = 0;
	}
}

- (void)startAnimation
{
	animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}

- (void)stopAnimation
{
	[animationTimer invalidate];
	animationTimer = nil;
}

- (void)setAnimationInterval:(NSTimeInterval)interval
{
	animationInterval = interval;
	
	if(animationTimer)
	{
		[self stopAnimation];
		[self startAnimation];
	}
}

- (NSTimeInterval) animationInterval{
    return animationInterval;
}

// Updates the OpenGL view when the timer fires
- (void)drawView
{
	
	// Make sure that you are drawing to the current context
	[EAGLContext setCurrentContext:context];
	
	// If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again.
	if(!controllerSetup)
	{
		
		//[controller setupView];
		[self setupView];
		controllerSetup = YES;
	}
	
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

	//[controller drawView:self];
	[self drawView: self];
	
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
	[context presentRenderbuffer:GL_RENDERBUFFER_OES];
	
	GLenum err = glGetError();
	if(err)
		NSLog(@"%x error", err);
}

// Stop animating and release resources when they are no longer needed.
- (void)dealloc
{
	[self stopAnimation];
	
	if([EAGLContext currentContext] == context)
	{
		[EAGLContext setCurrentContext:nil];
	}
	
	[context release];
	context = nil;
    
    free(vertices);
    free(icosahedronFaces);
    [motionManager stopDeviceMotionUpdates];
    [motionManager release];

	[super dealloc];
}
-(void)setupView
{
	
	const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0; 
	GLfloat size; 
	glEnable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION); 
	size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); 
	CGRect rect = self.bounds;
	glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / 
			   (rect.size.width / rect.size.height), zNear, zFar); 
	glViewport(0, 0, rect.size.width, rect.size.height);  
	glMatrixMode(GL_MODELVIEW);
    
    /*
    // Enable lighting
    glEnable(GL_LIGHTING);
	
    // Turn the first light on
    glEnable(GL_LIGHT0);
    
    // Define the ambient component of the first light
    static const Color3D light0Ambient[] = {{0.05, 0.05, 0.05, 1.0}};
	glLightfv(GL_LIGHT0, GL_AMBIENT, (const GLfloat *)light0Ambient);
    
    // Define the diffuse component of the first light
    static const Color3D light0Diffuse[] = {{0.4, 0.4, 0.4, 1.0}};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, (const GLfloat *)light0Diffuse);
    
    // Define the specular component and shininess of the first light
    static const Color3D light0Specular[] = {{0.7, 0.7, 0.7, 1.0}};
    glLightfv(GL_LIGHT0, GL_SPECULAR, (const GLfloat *)light0Specular);
    
    // Define the position of the first light
	// const GLfloat light0Position[] = {10.0, 10.0, 10.0}; 
    static const Vertex3D light0Position[] = {{10.0, 10.0, 10.0}};
	glLightfv(GL_LIGHT0, GL_POSITION, (const GLfloat *)light0Position); 
	
    // Calculate light vector so it points at the object
    static const Vertex3D objectPoint[] = {{0.0, 0.0, -3.0}};
    const Vertex3D lightVector = Vector3DMakeWithStartAndEndPoints(light0Position[0], objectPoint[0]);
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (GLfloat *)&lightVector);
	
    // Define a cutoff angle. This defines a 90° field of vision, since the cutoff
    // is number of degrees to each side of an imaginary line drawn from the light's
    // position along the vector supplied in GL_SPOT_DIRECTION above
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 25.0);
     */
    
	glLoadIdentity(); 
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
    lineSpace = 0.05;
    pointCount = 8/lineSpace + 4;
    vertices = malloc(sizeof(Vertex3D) * pointCount);
    float z = -0.1;
    
    int p = 0;
    for(float i = -1.0;i <1.0 + lineSpace;i+=lineSpace){
        Vertex3DSet(&vertices[p], i, -1, z);
        p++;
        Vertex3DSet(&vertices[p], i, 1, z);
        p++;
    }
    for (float j = -1.0; j <= 1.0 + lineSpace; j+=lineSpace) {
        Vertex3DSet(&vertices[p], -1, j, z);
        p++;
        Vertex3DSet(&vertices[p], 1, j, z);
        p++;
    }
    
    icosahedronFaces =  malloc(sizeof(GLubyte) * pointCount);
    for (int i = 0; i < pointCount; i++) {
        icosahedronFaces[i] = i;
    }
    

    motionManager = [[CMMotionManager alloc]init];
    if(motionManager.deviceMotionAvailable){
        motionManager.deviceMotionUpdateInterval = .01;
        [motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical toQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
            
            if(motion){
                CMAttitude *attitude = motion.attitude;
                roll_degree = -RADIANS_TO_DEGREES(attitude.roll);
                pitch_degree = -RADIANS_TO_DEGREES(attitude.pitch);
                yaw_degree = -RADIANS_TO_DEGREES(attitude.yaw);
            }
        }];
    }
}
- (void)drawView:(UIView*)view
{
    //static GLfloat rot = 0.0;
    
    // This is the same result as using Vertex3D, just faster to type and
    // can be made const this way
    
      
    glLoadIdentity();
    //glTranslatef(0.0f,0.0f,-3.0f);
    //glScalef(100, 100, 100);

    //glRotatef(45,1.0f,0.0f,0.0f);
    
    /*
    CMRotationMatrix rotation;
    CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
    if(deviceMotion){
        CMAttitude *attitude = deviceMotion.attitude;
        rotation = attitude.rotationMatrix;
        
     
        NSLog(@"pitch:%f",RADIANS_TO_DEGREES(attitude.pitch));
        NSLog(@"roll:%f",RADIANS_TO_DEGREES(attitude.roll));
        NSLog(@"yaw:%f",RADIANS_TO_DEGREES(attitude.yaw));
        
        Matrix3D matrix0,matrix1,matrix2,matrix;
        Matrix3DSetRotationByRadians(matrix0, -attitude.pitch, 1.0, 0.0, 0.0);
        Matrix3DSetRotationByRadians(matrix1, -attitude.roll, 0.0, 1.0, 0.0);
        Matrix3DSetRotationByRadians(matrix2, -attitude.yaw, 0.0, 0.0, 1.0);
        Matrix3DMultiply(matrix0, matrix1, matrix);
        Matrix3DMultiply(matrix, matrix2, matrix);
        glLoadMatrixf(matrix);
    
        
        
        glRotatef(-RADIANS_TO_DEGREES(attitude.roll), 0.0, 1.0, 0.0);   //x
        glRotatef(-RADIANS_TO_DEGREES(attitude.pitch), 1.0, 0.0, 0.0);  //y
        glRotatef(-RADIANS_TO_DEGREES(attitude.yaw), 0.0, 0.0, 1.0);
       
        
    
    }
     */
    
    glRotatef(roll_degree, 0.0, 1.0, 0.0);   //x
    glRotatef(pitch_degree, 1.0, 0.0, 0.0);  //y
    glRotatef(yaw_degree, 0.0, 0.0, 1.0);
    

    
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnableClientState(GL_VERTEX_ARRAY);
	glEnable(GL_COLOR_MATERIAL);
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glColor4f(1.0, 1.0, 1.0, 1.0);
    glDrawElements(GL_LINES, pointCount, GL_UNSIGNED_BYTE, icosahedronFaces);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_COLOR_MATERIAL);

}

@end
